﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
using RestEntities;
using System.Net;
using System.Security.Cryptography;

namespace WcfRestLib
{
	[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
		ConcurrencyMode = ConcurrencyMode.Single)]
	public class RestService : IRestService
	{
		private List<Person> RegisteredUsers;

		private object syncRoot = new object();

		public RestService()
		{
			RegisteredUsers = new List<Person>();

			List<Comment> comments = new List<Comment>(){ new Comment(){ CommentHead="tmp1", Time=DateTime.Now, CommentBody="body1" },
														new Comment(){ CommentHead="tmp2", Time=DateTime.Now, CommentBody="body2" },
														new Comment(){ CommentHead="tmp3", Time=DateTime.Now, CommentBody="body3"}
														};

			Person P = new Person() { Name = "Cawa", LastName = "Temp", Id = Person.GetNextPersonId(), Comments = comments };

			RegisteredUsers.Add(P);

			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Service sarted" });
		}

		public Person GetPersonById(string id)
		{
			Person p = null;
			lock (syncRoot)
			{
				WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { WebOperationContext.Current.IncomingRequest.ToString(), id });

				Guid guid;
				if (Guid.TryParse(id, out guid))
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return new Person();
				}

				p = (from item in RegisteredUsers
					 where item.Id == guid
					 select item).FirstOrDefault();

				if (p == null)
				{
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return new Person();
				}
			}

			return p;
		}

		public List<Person> GetPersonByName(string name)
		{
			List<Person> persons = null;
			lock (syncRoot)
			{
				persons = (from item in RegisteredUsers
						   where item.Name == name
						   select item).ToList();

				if (persons == null && persons.Count == 0)
				{
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return new List<Person>();
				}
			}

			return persons;
		}

		public List<Comment> GetUserCommentsByName(string name)
		{
			List<Comment> comments = null;
			lock (syncRoot)
			{
				comments = (from item in RegisteredUsers
							where item.Name == name
							select item.Comments).FirstOrDefault();

				if (comments == null)
				{
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return new List<Comment>();
				}
			}

			return comments;
		}

		public List<Comment> GetUserCommentsById(string id)
		{
			List<Comment> comments = null;
			Guid guid;

			lock (syncRoot)
			{

				if (!Guid.TryParse(id, out guid))
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return new List<Comment>();
				}

				comments = (from item in RegisteredUsers
							where item.Id == guid
							select item.Comments).FirstOrDefault();

				if (comments == null)
				{
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return new List<Comment>();
				}
			}

			return comments;
		}

		public List<Person> GetAllPersons()
		{
			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Get all persons" });
			return RegisteredUsers;
		}


		public void CreatePerson(Person person)
		{
			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Create person" });
			lock (syncRoot)
			{
				if (person == null)
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return;
				}

				UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;
				UriTemplate template = new UriTemplate(MyRestUriTemplate.Person);
				person.Uri = template.BindByPosition(match.BaseUri, person.Id.ToString());

				RegisteredUsers.Add(person);

				WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(person.Uri);
			}
		}

		public void UpdatePerson(string pid, Person person)
		{
			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Update person begin", pid });

			lock (syncRoot)
			{
				Guid guid;
				if (!Guid.TryParse(pid, out guid))
				{
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Failed to parse pid ->", pid });
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return;
				}

				Person existing = (from pers in RegisteredUsers
								   where pers.Id == guid
								   select pers).FirstOrDefault();

				if (existing == null)
				{
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Person not found", pid });
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return;
				}

				existing.Update(person);
				WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
			}
		}

		public void DeletePerson(string pid)
		{
			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Begin Delete person", pid });
			Guid guid;
			lock (syncRoot)
			{
				if (!Guid.TryParse(pid, out guid))
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return;
				}

				Person existingPerson = (from item in RegisteredUsers
										 where item.Id == guid
										 select item).FirstOrDefault();

				if (existingPerson == null || !RegisteredUsers.Contains(existingPerson))
				{
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return;
				}

				RegisteredUsers.Remove(existingPerson);
				WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
				WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "person successfully deleted", pid });
			}
		}


		public void CreateComment(string pid, Comment comment)
		{
			WebOperationContext ctx = WebOperationContext.Current;
			Guid guid;

			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Begin add comment ..." });
			lock (syncRoot)
			{
				if (comment == null)
				{
					ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ExpectationFailed;
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Add comment error object is null !!!!!!" });
					return;
				}
				if (string.IsNullOrEmpty(pid) || !Guid.TryParse(pid, out guid))
				{
					ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ExpectationFailed;
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Add comment error pid is incorrect !!!!!!" });
					return;
				}

				Person pers = (from p in RegisteredUsers
							   where p.Id == guid
							   select p).FirstOrDefault();
				if (pers == null)
				{
					ctx.OutgoingResponse.SetStatusAsNotFound();
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Cannot find person with specify Id !!!!!!" });
					return;
				}

				pers.Comments.Add(comment);
				WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Comment successfully added " });

				ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
			}
		}

		public void UpdateComment(string cid, Comment comment)
		{
			Guid id;
			bool found = false;

			lock (syncRoot)
			{

				if (!Guid.TryParse(cid, out id))
				{
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Incorrect comment id " });
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return;
				}

				foreach (Person p in RegisteredUsers)
				{
					Comment com = (from c in p.Comments
								   where c.CommentId == id
								   select c).FirstOrDefault();

					if (com != null)
					{
						found = true;
						com.Update(comment);
						WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Comment updated successfully." });
						WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
						break;
					}
				}
				if (!found)
				{
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Comment to delete not found !!!!" });
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return;
				}
			}
		}

		public void DeleteComment(string cid)
		{
			Guid guid;
			bool found = false;

			lock (syncRoot)
			{
				if (!Guid.TryParse(cid, out guid))
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Incorrect cid " });
					return;
				}

				foreach (Person p in RegisteredUsers)
				{
					Comment com = (from c in p.Comments
								   where c.CommentId == guid
								   select c).FirstOrDefault();

					if (com != null)
					{
						found = true;
						p.Comments.Remove(com);
						WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
						WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Comment deleted successfully" });
						break;
					}
				}

				if (!found)
				{
					WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Comment to delete not found" });
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return;
				}
			}
		}

		private bool IsUserAuthenticated(string username)
		{
			WebOperationContext context = WebOperationContext.Current;
			IncomingWebRequestContext incomingRequestContext = context.IncomingRequest;
			string requestUri = incomingRequestContext.UriTemplateMatch.RequestUri.ToString();
			string authorizationHeader = incomingRequestContext.Headers[HttpRequestHeader.Authorization];
			string authenticationToekn = string.Format("{1}{0}{2}", "-", authorizationHeader, requestUri);
			byte[] userKey = GetUserKey(username);
			bool isValidHash = SystemHelperLibrary.FileExt.FileHelper.ValidateHash(userKey, authenticationToekn, "-", UTF8Encoding.UTF8);
			return isValidHash;
		}

		private byte[] GetUserKey(string username)
		{
			Byte[] saltBytes = new Byte[4];
			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			rng.GetBytes(saltBytes);

			return saltBytes;
		}


		public List<Person> GetAllPersonsJson()
		{
			return this.RegisteredUsers;
		}

		public Person GetPersonJson(string id)
		{
			Person p = null;
			lock (syncRoot)
			{
				WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { WebOperationContext.Current.IncomingRequest.ToString(), id });

				Guid guid;
				if (Guid.TryParse(id, out guid))
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return new Person();
				}

				p = (from item in RegisteredUsers
					 where item.Id == guid
					 select item).FirstOrDefault();

				if (p == null)
				{
					WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
					return new Person();
				}
			}

			return p;
		}


		public void CreateJsonPerson(Person person)
		{
			WcfLogger.WcfLogger.Instance.WriteToLog(new string[] { "Create person" });
			lock (syncRoot)
			{
				if (person == null)
				{
					WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
					return;
				}

				UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;
				UriTemplate template = new UriTemplate(MyRestUriTemplate.Person);
				person.Uri = template.BindByPosition(match.BaseUri, person.Id.ToString());

				RegisteredUsers.Add(person);

				WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(person.Uri);
			}
		}
	}
}
